﻿using CRMTestApp.CRMTestCode;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Messages;
using Microsoft.Xrm.Sdk.Query;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Xml;
using System.Reflection.Emit;
using Microsoft.Xrm.Sdk.Metadata;
using TypeBuilderNamespace;
using System.Runtime.Remoting;
using System.Reflection;

namespace CRMTestApp.DownloadFiles
{
    class ExportEntityData : ITestCode
    {
        private string _EntityName;
        public ExportEntityData(string EntityName)
        {
            _EntityName = EntityName;
        }

        public void Activate(Microsoft.Xrm.Sdk.IOrganizationService service, System.ComponentModel.BackgroundWorker bw)
        {
            string RootStorePath = string.Format("{0}", _EntityName.Replace("Download", "Downloaded"));
            ServiceContext _context = new ServiceContext(service);
            int i = 1;

            if (_EntityName == "downloademailattachment")
            {
                string FetchXML = string.Format(@"<fetch version='1.0' output-format='xml-platform' mapping='logical' distinct='false'>
                                  <entity name='{0}'>
                                    <attribute name='activityid'/>
                                  </entity>
                                </fetch>", "email");

                List<Guid> Emails = RetrieveFetchedData(service, bw, FetchXML).Select(p => (Guid)p.Attributes["activityid"]).ToList<Guid>();

                foreach (Guid EmailID in Emails.Take(50))
                {
                    List<Guid> Attachments = (from email in _context.EmailSet
                                              join attachment in _context.ActivityMimeAttachmentSet
                                              on email.ActivityId equals attachment.ObjectId.Id
                                              where email.ActivityId == EmailID
                                              select attachment.ActivityMimeAttachmentId.Value).ToList<Guid>();

                    foreach (Guid Attachment in Attachments)
                    {
                        ActivityMimeAttachment Object = service.Retrieve(ActivityMimeAttachment.EntityLogicalName, Attachment, new Microsoft.Xrm.Sdk.Query.ColumnSet("body", "filename", "objectid")).ToEntity<ActivityMimeAttachment>();

                        string FolderName = string.Format(@"{0}\{1}", RootStorePath, Object.ObjectId.Id);

                        if (!Directory.Exists(FolderName))
                        {
                            Directory.CreateDirectory(FolderName);
                        }

                        string FileName = string.Format(@"{0}\{1}", FolderName, Object.FileName);

                        using (FileStream stream = new FileStream(FileName, FileMode.OpenOrCreate))
                        {
                            byte[] fileContext = Convert.FromBase64String(Object.Body);
                            stream.Write(fileContext, 0, fileContext.Length);
                        }
                    }

                    bw.ReportProgress(0, string.Format("{0} out of {1} emails attachment downloaded successfully", i, Emails.Count));
                    i++;
                }
            }
            else if (_EntityName == "downloadnoteattachment")
            {
                string FetchXML = string.Format(@"<fetch version='1.0' output-format='xml-platform' mapping='logical' distinct='false'>
                                  <entity name='{0}'>
                                    <attribute name='annotationid'/>
                                  </entity>
                                </fetch>", "annotation");

                List<Guid> Notes = RetrieveFetchedData(service, bw, FetchXML).Select(p => (Guid)p.Attributes["annotationid"]).ToList<Guid>();


                foreach (Guid NoteID in Notes)
                {
                    Annotation Object = service.Retrieve(Annotation.EntityLogicalName, NoteID, new Microsoft.Xrm.Sdk.Query.ColumnSet("documentbody", "filename")).ToEntity<Annotation>();

                    if (!String.IsNullOrEmpty(Object.DocumentBody))
                    {

                        string FolderName = string.Format(@"{0}\{1}", RootStorePath, Object.AnnotationId.Value);

                        if (!Directory.Exists(FolderName))
                        {
                            Directory.CreateDirectory(FolderName);
                        }

                        string FileName = string.Format(@"{0}\{1}", FolderName, Object.FileName);

                        using (FileStream stream = new FileStream(FileName, FileMode.OpenOrCreate))
                        {
                            byte[] fileContext = Convert.FromBase64String(Object.DocumentBody);
                            stream.Write(fileContext, 0, fileContext.Length);
                        }
                    }

                    bw.ReportProgress(0, string.Format("{0} out of {1} notes attachment downloaded successfully", i, Notes.Count));
                    i++;
                }
            }
            else
            {
                string FetchXML =  string.Format(@"<fetch version='1.0' output-format='xml-platform' mapping='logical' distinct='false'>
                                  <entity name='{0}'>
                                    <all-attributes/>
                                  </entity>
                                </fetch>", _EntityName);

                List<Entity> Records = RetrieveFetchedData(service, bw, FetchXML);

                if (Records.Count > 0)
                {
                    StringBuilder sb = new StringBuilder();

                    RetrieveEntityRequest entityReq = new RetrieveEntityRequest()
                    {
                        LogicalName = _EntityName,
                        EntityFilters = Microsoft.Xrm.Sdk.Metadata.EntityFilters.Attributes
                    };

                    RetrieveEntityResponse resp = (RetrieveEntityResponse)service.Execute(entityReq);

                    List<string> Attributes = new List<string>();

                    foreach (AttributeMetadata attribute in resp.EntityMetadata.Attributes)
                    {
                        switch (attribute.AttributeType)
                        {
                            case AttributeTypeCode.Customer:
                            case AttributeTypeCode.Lookup:
                            case AttributeTypeCode.Owner:
                                Attributes.AddRange(new string[] { attribute.LogicalName, string.Format("{0}name", attribute.LogicalName), string.Format("{0}type", attribute.LogicalName) });
                                break;

                            case AttributeTypeCode.Picklist:
                            case AttributeTypeCode.State:
                            case AttributeTypeCode.Status:
                                Attributes.AddRange(new string[] { attribute.LogicalName, string.Format("{0}name", attribute.LogicalName) });
                                break;

                            default:
                                Attributes.Add(attribute.LogicalName);
                                break;
                        }
                    }

                    StringBuilder output = new StringBuilder();

                    List<object> GenericList = new List<object>();

                    string Header = "";

                    string[] arr = Attributes.ToArray();

                    foreach (PropertyInfo prop in MyTypeBuilder.CompileResultType(arr).GetProperties())
                    {
                        Header = Header + string.Format(",{0}", prop.Name);
                    }
                    output.AppendLine(Header.Substring(1));

                    var NewObj = Activator.CreateInstance(MyTypeBuilder.CompileResultType(Attributes.ToArray()));
                    Type t = NewObj.GetType();

                    foreach (Entity Record in Records)
                    {
                        foreach (PropertyInfo clearprop in NewObj.GetType().GetProperties())
                        {
                            clearprop.SetValue(NewObj, "", null);
                        }

                        foreach (KeyValuePair<string, object> pair in Record.Attributes)
                        {
                            switch (pair.Value.GetType().Name)
                            {
                                case "EntityReference":
                                    t.GetProperty(pair.Key).SetValue(NewObj, ((EntityReference)pair.Value).Id.ToString(), null);
                                    t.GetProperty(string.Format("{0}name", pair.Key)).SetValue(NewObj, ((EntityReference)pair.Value).Name, null);
                                    t.GetProperty(string.Format("{0}type", pair.Key)).SetValue(NewObj, ((EntityReference)pair.Value).LogicalName, null);
                                    break;

                                case "Boolean":
                                case "DateTime":
                                case "Decimal":
                                case "String":
                                case "Int32":
                                    t.GetProperty(pair.Key).SetValue(NewObj, pair.Value.ToString(), null);
                                    break;

                                case "Guid":
                                    t.GetProperty(pair.Key).SetValue(NewObj, ((Guid)pair.Value).ToString(), null);
                                    break;

                                case "Money":
                                    t.GetProperty(pair.Key).SetValue(NewObj, ((Money)pair.Value).Value.ToString(), null);
                                    break;

                                case "OptionSetValue":
                                    t.GetProperty(pair.Key).SetValue(NewObj, ((OptionSetValue)pair.Value).Value.ToString(), null);

                                    if (Record.FormattedValues.Contains(pair.Key) && Record.FormattedValues[pair.Key] != null)
                                        t.GetProperty(string.Format("{0}name", pair.Key)).SetValue(NewObj, Record.FormattedValues[pair.Key].ToString(), null);

                                    break;
                            }
                        }

                        string RowGenerator = string.Empty;
                        foreach (PropertyInfo prop in NewObj.GetType().GetProperties())
                        {
                            RowGenerator = RowGenerator + string.Format(",\"{0}\"", prop.GetValue(NewObj, null));
                        }
                        output.AppendLine(RowGenerator.Substring(1));

                        bw.ReportProgress(0, string.Format("{0} out of {1} records processed successfully", i, Records.Count));
                        i++;
                    }

                    File.WriteAllText(string.Format("{0}.csv", _EntityName), output.ToString());
                    bw.Dispose();
                }
            }
        }

        private List<Entity> RetrieveFetchedData(Microsoft.Xrm.Sdk.IOrganizationService service, System.ComponentModel.BackgroundWorker bw,string fetchXML)
        {
            string fetchxml = fetchXML;

            int fetchcount = 250;
            int pagenumber = 1;
            int recordcount = 0;

            string pagecookie = null;

            List<Entity> Records = new List<Entity>();

            while (true)
            {
                string xml = CreateXml(fetchxml, pagecookie, pagenumber, fetchcount);

                RetrieveMultipleRequest req = new RetrieveMultipleRequest()
                {
                    Query = new FetchExpression(xml)
                };

                EntityCollection collection = ((RetrieveMultipleResponse)service.Execute(req)).EntityCollection;

                Records.AddRange(collection.Entities);

                bw.ReportProgress(0, string.Format("{0} {1} records fetched. (pages processed : {2})", Records.Count, _EntityName.Replace("download", ""), pagenumber));

                recordcount = recordcount + collection.Entities.Count;

                if (collection.MoreRecords)
                {
                    pagenumber++;
                    pagecookie = collection.PagingCookie;
                }
                else
                {
                    break;
                }

            }
            return Records;
        }

        public string CreateXml(string xml, string cookie, int page, int count)
        {
            StringReader stringReader = new StringReader(xml);
            XmlTextReader reader = new XmlTextReader(stringReader);

            // Load document
            XmlDocument doc = new XmlDocument();
            doc.Load(reader);

            return CreateXml(doc, cookie, page, count);
        }

        public string CreateXml(XmlDocument doc, string cookie, int page, int count)
        {
            XmlAttributeCollection attrs = doc.DocumentElement.Attributes;

            if (cookie != null)
            {
                XmlAttribute pagingAttr = doc.CreateAttribute("paging-cookie");
                pagingAttr.Value = cookie;
                attrs.Append(pagingAttr);
            }

            XmlAttribute pageAttr = doc.CreateAttribute("page");
            pageAttr.Value = System.Convert.ToString(page);
            attrs.Append(pageAttr);

            XmlAttribute countAttr = doc.CreateAttribute("count");
            countAttr.Value = System.Convert.ToString(count);
            attrs.Append(countAttr);

            StringBuilder sb = new StringBuilder(1024);
            StringWriter stringWriter = new StringWriter(sb);

            XmlTextWriter writer = new XmlTextWriter(stringWriter);
            doc.WriteTo(writer);
            writer.Close();

            return sb.ToString();
        }
    }
}
